<?php
/**
 * Created by PhpStorm.
 * User: admin
 * Date: 2019/5/6
 * Time: 10:19
 */

namespace iBrand\Shop\Server\Controllers\V2;


use Carbon\Carbon;
use iBrand\Component\Balance\Repository\BalanceRepository;
use iBrand\Component\Discount\Distributors\IntegerDistributor;
use iBrand\Component\Goods\Models\GoodsProduct;
use iBrand\Component\Order\Models\Adjustment;
use iBrand\Component\Order\Models\Order;
use iBrand\Component\Order\Models\OrderItem;
use iBrand\Component\Order\Processor\OrderProcessor;
use iBrand\Component\Order\Repositories\OrderRepository;
use iBrand\Component\Payment\Models\Payment;
use iBrand\Shop\Core\Models\Balance;
use iBrand\Shop\Core\Models\CashRecord;
use iBrand\Shop\Core\Models\O2oGoods;
use iBrand\Shop\Core\Models\OrderRefundRelation;
use iBrand\Shop\Core\Models\PaymentDetail;
use iBrand\Shop\Core\Models\Refund;
use iBrand\Shop\Core\Models\RefundItem;
use iBrand\Shop\Core\Models\RefundPayment;
use iBrand\Shop\Core\Models\User;
use iBrand\Shop\Core\Processor\ShopRefundProcessor;
use iBrand\Shop\Core\Repositories\CashRecordRepository;
use iBrand\Shop\Core\Repositories\ShopOrderRepository;
use iBrand\Shop\Server\Service\GoodsService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Validator;

class RefundController extends \iBrand\Common\Controllers\Controller
{
    protected $orderRepository;
    protected $orderProcessor;
    protected $balanceRepository;
    protected $refundProcessor;
    protected $shopOrderRepository;
    protected $goodsServices;
    protected $cashRecordRepository;
    protected $distributor;

    public function __construct(OrderRepository $orderRepository,
                                OrderProcessor $orderProcessor,
                                BalanceRepository $balanceRepository,
                                ShopRefundProcessor $shopRefundProcessor,
                                ShopOrderRepository $shopOrderRepository,
                                GoodsService $goodsServices,
                                CashRecordRepository $cashRecordRepository,
                                IntegerDistributor $distributor)
    {
        $this->orderRepository = $orderRepository;
        $this->orderProcessor = $orderProcessor;
        $this->balanceRepository = $balanceRepository;
        $this->refundProcessor = $shopRefundProcessor;
        $this->shopOrderRepository = $shopOrderRepository;
        $this->goodsServices = $goodsServices;
        $this->cashRecordRepository = $cashRecordRepository;
        $this->distributor = $distributor;
    }

    public function apply(Request $request)
    {
        if (!$item_ids = $request->input('item_ids')) {
            return $this->failed('请选择需要售后的商品');
        }

        $orderItems = OrderItem::whereIn('id', $item_ids)->get();

        $reasons = settings('order_refund_reason');

        return $this->success(['order_items' => $orderItems, 'reasons' => $reasons]);

    }

    /**
     * 提交售后申请
     * @param Request $request
     * @return mixed
     */
    public function checkout(Request $request)
    {
        //refund对象：order_id, refund_items:[[order_item_id_1,quantity_1],[order_item_id_2,quantity_2]], content

        //新的订单对象： order_items

        $input = $request->except('_token', 'file');

        $validator = $this->validator();
        if ($validator->fails()) {
            return $this->failed($validator->messages()->first());
        }

        $clerk = auth('clerk')->user();
        $order = $this->orderRepository->with(['items'])->findByField('order_no', $input['order_no'])->first();

        $refundData = $input['refund'];
        $refundItems = $refundData['refund_items'];

        $orderData = $input['order'];
        $orderItems = $orderData['order_items'];

        if (!$order) {
            return $this->failed('订单不存在');
        }

        $type = 1;  //退货
        if (count($orderItems)) {
            $type = 2;  //换货
        }

        try {
            DB::beginTransaction();
            $user_id = $order->user_id;

            $refund = new Refund([
                'user_id' => $user_id,
                'order_id' => $order->id,
                'content' => $refundData['content'],
                'type' => $type,
                'shop_id' => $order->channel_id
            ]);

            $refund = $this->buildRefundItem($refundItems, $refund);

            if (abs($refund->amount - $refundData['refund_amount']) >= 100) {  //如果前端提交的退款金额和后端计算的金额误差在1元
                return $this->failed('退款金额有误');
            }

            $needPay = 0;
            $exchangeOrder = null;
            if (count($orderItems)) {
                $exchangeOrder = new Order(['user_id' => $user_id, 'type' => Order::TYPE_SHOP_REFUND, 'channel' => 'shop', 'channel_id' => $clerk->shop_id, 'submit_time' => Carbon::now()]);
                $exchangeOrder = $this->BuildOrderItems($orderItems, $exchangeOrder);

                $needPay = $exchangeOrder->getNeedPayAmount();

                if ($needPay != $orderData['pay_amount']) {
                    throw new \Exception('支付金额错误');
                }

                if (!$exchangeOrder = $this->orderProcessor->create($exchangeOrder)) {
                    throw new \Exception('订单提交失败，请确认后重试');
                }
            }

            $difference = $refund->amount - $needPay;  //退货差价

            if ($difference > 0) {
                $actual_amount = floor($difference / 100);
                $refund->actual_amount = $actual_amount * 100;
            }

            if (!$refund = $this->refundProcessor->create($refund)) {
                throw new \Exception('售后提交失败，请确认后重试');
            }

            DB::commit();

            $balance = $this->balanceRepository->getSum($user_id);
            $user = User::find($user_id);

            return $this->success(['order' => $order,
                'balance' => $balance,
                'needPay' => $difference <= 0 ? abs($difference) : 0,
                'refund' => $refund,
                'difference' => $difference,
                'user' => $user,
                'type' => $type,
                'exchangeOrder' => $exchangeOrder
            ]);
        } catch (\Exception $exception) {
            DB::rollBack();
            \Log::info($exception->getTraceAsString());
            \Log::info($exception->getMessage());
            return $this->failed($exception->getMessage());
        }
    }

    private function validator()
    {
        $rules = [
            'order_no' => 'required',
            'refund.refund_items' => 'required',
            'refund.refund_amount' => 'required|integer|min:1'
        ];
        $message = [
            "required" => ":attribute 不能为空",
            "integer" => ":attribute 必须为整数",
            "array" => ":attribute 不能为空",
            'refund.refund_amount' => ':attribute 必须大于0',
            'order.order_items.*.num' => ":attribute 必须大于0",
            'order.order_item.*.goods_id' => ':attribute 不能为空'
        ];
        $attributes = [
            'order_no' => '退货订单编号',
            'refund.refund_items' => '退货信息',
            'user_id' => '用户id',
            'refund.refund_amount' => '退款金额',
            'order.order_item.*.num' => '商品数量',
            'order.order_item.*.goods_id' => '商品信息'
        ];

        $validator = Validator::make(\request()->all(), $rules, $message, $attributes);

        $validator->sometimes('order.order_items.*.num', 'required|integer', function ($input) {
            return count($input->order['order_items']) > 0;
        });

        $validator->sometimes('order.order_items.*.goods_id', 'required', function ($input) {
            return count($input->order['order_items']) > 0;
        });

        return $validator;
    }

    private function buildRefundItem($refundItems, $refund)
    {
        foreach ($refundItems as $item) {
            $orderItem = OrderItem::find($item['order_item_id']);

            if ($orderItem->quantity == $item['quantity']) { //如果提交的售后数量和订单的item数量相等
                $amount = $orderItem->total;
            } else {
                $amount = floor(($orderItem->total / $orderItem->quantity) * $item['quantity']); //向下取整
            }

            $refundItem = new RefundItem([
                'quantity' => $item['quantity'],
                'amount' => $amount,
                'order_item_id' => $item['order_item_id']
            ]);
            $refund->addItem($refundItem);
        }
        return $refund;
    }

    private function BuildOrderItems($orderItems, $order)
    {
        $adjustments = [];
        foreach ($orderItems as $item) {
            $item_meta = [
                'image' => $item['base_goods']['img'],
                'detail_id' => $item['base_goods']['id'],
                'dynamic_sku' => $item['base_product']['sku'],
                'specs_text' => implode('-', $item['spec_text']),
            ];

            $o2oGoods = O2oGoods::where('goods_id', $item['base_goods']['id'])->where('shop_id', $order->channel_id)->first();
            $unit_price = $o2oGoods->goods->sell_price * 100;  //以分为单位
            if ($item['user_discount'] < 100) {
                $adjustments_total = (($unit_price * (100 - $item['user_discount']) / 100)) * $item['num'];

                //根据折扣，计算小计,和前端一致保留2位小数
                $calculateTotal = sprintf("%.2f", $unit_price * ($item['user_discount'] / 100) * $item['num']);

                $difference = abs($calculateTotal - $item['subtotal_cent']);
                if ($difference > 0 AND $difference < 100) {  //如果和前端的小计有1元以内差额，优惠以前端为准
                    $adjustments_total = $unit_price * $item['num'] - $item['subtotal_cent'];
                }

                $adjustments[$item['base_product']['id']]['amount'] = -$adjustments_total;
                $adjustments[$item['base_product']['id']]['discount_value'] = $item['user_discount'];

            }

            $orderItem = new OrderItem([
                'quantity' => $item['num'],
                'unit_price' => $unit_price / 100,
                'item_id' => $item['base_product']['id'],
                'type' => GoodsProduct::class,
                'item_name' => $item['base_goods']['text'],
                'item_meta' => $item_meta
            ]);

            $orderItem->recalculateUnitsTotal();
            $orderItem->save();
            $order->addItem($orderItem);
        }

        foreach ($order->getItems() as $item) {
            if (isset($adjustments[$item->item_id])) {
                $adjustment = new Adjustment([
                    'order_id' => $order->id,
                    'order_item_id' => $item->id,
                    'type' => Adjustment::ORDER_ITEM_DISCOUNT_ADJUSTMENT,
                    'label' => '会员折扣',
                    'origin_type' => 'user_order_discount',
                    'origin_id' => $order->user_id,
                    'origin_value' => $adjustments[$item->item_id]['discount_value'],
                    'amount' => $adjustments[$item->item_id]['amount']
                ]);
                $order->addAdjustment($adjustment);

                $item->item_discount += $adjustments[$item->item_id]['amount'];
                $item->recalculateAdjustmentsTotal();
            }
        }
        return $order;
    }

    public function confirm(Request $request)
    {
        $input = $request->except('_token', 'file');
        $user_id = $input['user_id'];

        $clerk = auth('clerk')->user();

        $refund = Refund::find($input['refund_id']);

        $order = $this->orderRepository->find($input['order_id']);
        $exchangeOrder = null;
        $difference = 0;
        $type = 1; //需退款
        if ($exchangeOrderID = $input['exchange_order_id']) {
            $exchangeOrder = $this->orderRepository->find($exchangeOrderID);
            if (!$exchangeOrder) {
                return $this->failed('订单不存在');
            }

            if ($input['wipe'] > 0) {
                $adjustment = new Adjustment([
                    'type' => Adjustment::ORDER_WIPE_DISCOUNT_ADJUSTMENT,
                    'label' => '抹零',
                    'origin_type' => 'wipe',
                    'origin_id' => $order->user_id,
                    'amount' => -$input['wipe'],
                ]);

                $exchangeOrder->addAdjustment($adjustment);

                $splitDiscountAmount = $this->distributor->distributePercentageShopOrder(-$input['wipe'], $exchangeOrder);

                $i = 0;
                foreach ($exchangeOrder->getItems() as $item) {
                    if (!is_int($item->getTotal() / 100)) {
                        $item->divide_order_discount += $splitDiscountAmount[$i++];
                        $item->recalculateAdjustmentsTotal();
                    }
                }

            }

            $difference = $refund->amount - $exchangeOrder->total;   //退款金额 - 换货订单金额
            $type = $refund->actual_amount > 0 ? 1 : 2;  //1:需退款  2:需补款
        }


        if (count($input['payments']) == 0) {
            return $this->failed('无效的退款方式');
        }

        $sum = array_sum(array_column($input['payments'], 'amount'));

        /* if ($sum != $refund->actual_amount AND !$exchangeOrder) {*/
        if ($sum != $refund->actual_amount AND $type == 1) {
            return $this->failed('退款金额错误');
        }

        /*if ($difference > 0 AND abs($sum - $difference) >= 100) {*/
        if ($refund->actual_amount > 0 AND abs($sum - $refund->actual_amount) >= 100) {
            return $this->failed('退款金额错误');
        }

        try {
            DB::beginTransaction();

            if ($type == 1) { //需退款
                $this->calculateExchangeRefund($refund, $order, $exchangeOrder);
            } elseif ($type == 2) {
                $this->calculateRefundPay($refund, $exchangeOrder, $clerk);
            }

            if ($exchangeOrder) {
                $this->shopOrderRepository->create([
                    'order_id' => $exchangeOrder->id,
                    'clerk_id' => $clerk->id,
                    'shop_id' => $clerk->shop_id,
                ]);

                OrderRefundRelation::create([
                    'order_id' => $exchangeOrder->id,
                    'refund_id' => $refund->id,
                    'refund_amount' => $refund->amount,
                    'difference_amount' => $difference > 100 ? $difference : 0
                ]);
            }

            $this->goodsServices->refundAddStore($refund, $refund->shop_id);
            $this->goodsServices->exchangeReduceStore($exchangeOrder, $refund->shop_id);

            $refund->clerk_id = $input['clerk_id'];
            $refund->status = 1;
            $refund->save();

            event('shop.refund.completed.point', [$refund]);

            DB::commit();

            return $this->success(['refund' => $refund]);
        } catch (\Exception $exception) {
            DB::rollBack();

            \Log::info($exception->getMessage() . $exception->getTraceAsString());

            return $this->failed($exception->getMessage());
        }

    }

    /**
     * 换货+退款 / 纯退款
     * @param $refund
     * @param $exchangeOrder
     */
    private function calculateExchangeRefund($refund, $order, $exchangeOrder)
    {
        $user_id = $refund->user_id;
        $balance = $this->balanceRepository->getSum($user_id);

        foreach (\request('payments') as $item) {
            if ($item['key'] == RefundPayment::CHANNEL_BALANCE AND $item['amount'] > 0) {
                RefundPayment::create([
                    'refund_id' => $refund->id,
                    'amount' => $refund->actual_amount,
                    'origin_type' => RefundPayment::CHANNEL_BALANCE
                ]);

                $this->balanceRepository->addRecord([
                    'user_id' => $user_id,
                    'type' => Balance::TYPE_REFUND,
                    'note' => '订单:' . $order->order_no . ' 退款',
                    'value' => $refund->actual_amount,
                    'current_balance' => $balance + $refund->actual_amount,
                    'origin_id' => $refund->id,
                    'origin_type' => Refund::class,
                ]);
            }

            if ($item['key'] == RefundPayment::CHANNEL_CASH AND $item['amount'] > 0) {
                RefundPayment::create([
                    'refund_id' => $refund->id,
                    'amount' => $refund->actual_amount,
                    'origin_type' => RefundPayment::CHANNEL_CASH
                ]);
            }
        }

        if ($exchangeOrder) {  //换货+退款
            RefundPayment::create([
                'refund_id' => $refund->id,
                'amount' => $exchangeOrder->total,
                'origin_type' => RefundPayment::CHANNEL_ORDER
            ]);

            $payment = new Payment([
                'order_id' => $exchangeOrder->id,
                'order_type' => Order::class,
                'channel' => Payment::CHANNEL_REFUND,
                'amount' => $exchangeOrder->total,
                'status' => Payment::STATUS_COMPLETED,
                'paid_at' => Carbon::now(),
            ]);
            $exchangeOrder->payments()->save($payment);

            $exchangeOrder->pay_type = 'free';
            $exchangeOrder->status = Order::STATUS_PAY;
            $exchangeOrder->pay_time = Carbon::now();
            $exchangeOrder->pay_status = 1;
            $exchangeOrder->save();

            PaymentDetail::create([
                'order_id' => $exchangeOrder->id,
                'total_amount' => $exchangeOrder->total,   //总金额
                'wipe' => 0,  //抹零
                'actual' => 0,  //实收
                'remaining' => 0,  //找零
            ]);
        }
    }


    /**
     * 换货需要补款
     * @param $refund
     * @param $exchangeOrder
     * @param $clerk
     * @throws \Prettus\Validator\Exceptions\ValidatorException
     */
    private function calculateRefundPay($refund, $exchangeOrder, $clerk)
    {
        $user_id = $refund->user_id;
        $balance = $this->balanceRepository->getSum($user_id);
        foreach (\request('payments') as $item) {
            if ($item['key'] == Payment::CHANNEL_BALANCE && $item['amount'] > 0) {
                $balance = $this->balanceRepository->getSum($user_id);
                if ($balance < $item['amount']) {
                    throw  new \Exception('余额不足');
                    break;
                }
            }

            $amount = $item['amount'];
            if ($item['key'] == Payment::CHANNEL_CASH && $item['amount'] > 0) {
                $this->cashRecordRepository->create([
                    'item_type' => Order::class,
                    'item_id' => $exchangeOrder->id,
                    'clerk_id' => $clerk->id,
                    'shop_id' => $clerk->shop_id,
                    'amount' => $item['amount'],
                    'category' => CashRecord::CATEGORY_ORDER,
                ]);

                $amount = $item['amount'] - \request('remaining'); //只有现金支付才有可能存在找零
            }

            if ($item['amount'] > 0) {
                $payment = new Payment([
                    'order_id' => $exchangeOrder->id,
                    'order_type' => Order::class,
                    'channel' => $item['key'],
                    'amount' => $amount,
                    'status' => Payment::STATUS_COMPLETED,
                    'paid_at' => Carbon::now(),
                ]);
                $exchangeOrder->payments()->save($payment);
            }


            if ($item['key'] == Payment::CHANNEL_BALANCE && $item['amount'] > 0) {
                $this->balanceRepository->addRecord([
                    'user_id' => $user_id,
                    'type' => Balance::TYPE_ORDER_PAYMENT,
                    'note' => '订单余额支付：' . $item['amount'] / 100 . ',订单号：' . $exchangeOrder->order_no,
                    'value' => -$item['amount'],
                    'current_balance' => $balance - $item['amount'],
                    'origin_id' => $payment->id,
                    'origin_type' => Payment::class,
                ]);
            }
        }

        $payment = new Payment([
            'order_id' => $exchangeOrder->id,
            'order_type' => Order::class,
            'channel' => Payment::CHANNEL_REFUND,
            'amount' => $refund->amount,
            'status' => Payment::STATUS_COMPLETED,
            'paid_at' => Carbon::now(),
        ]);
        $exchangeOrder->payments()->save($payment);

        $exchangeOrder->pay_type = 'free';
        $exchangeOrder->status = Order::STATUS_PAY;
        $exchangeOrder->pay_time = Carbon::now();
        $exchangeOrder->pay_status = 1;
        $exchangeOrder->save();

        /*if ($refund->actual_amount > 0) {
            $actual = $exchangeOrder->total - $refund->actual_amount - \request('wipe') - \request('remaining');
        } else {
            $actual = $exchangeOrder->total - $refund->amount - \request('wipe') - \request('remaining');
        }*/

        PaymentDetail::create([
            'order_id' => $exchangeOrder->id,
            'total_amount' => $exchangeOrder->total,   //总金额
            'wipe' => \request('wipe'),  //抹零
            'actual' => \request('actual'),  //实收
            'remaining' => \request('remaining'),  //找零
        ]);
    }

    public function refundSuccess()
    {
        $refund_id = \request('refund_id');

        $refund = Refund::with(['payment', 'user', 'clerk'])->find($refund_id);

        $exchangeOrder = null;
        $paymentDetail = null;
        if ($relation = OrderRefundRelation::where('refund_id', $refund_id)->first()) {
            $exchangeOrder = Order::with('payments')->find($relation->order_id);
            $paymentDetail = PaymentDetail::where('order_id', $exchangeOrder->id)->first();
        }

        return $this->success(['refund' => $refund, 'exchangeOrder' => $exchangeOrder, 'paymentDetail' => $paymentDetail]);
    }

}